package fasilkom.antz.background;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.LinkedList;

import com.golden.gamedev.engine.BaseLoader;
import com.golden.gamedev.object.background.abstraction.AbstractTileBackground;

import fasilkom.antz.algo.PathFindingNode;


/**
 * Kelas ini mengimplementasikan {@link AbstractTileBackground} dengan menghubungkannya
 * dengan {@link Map}.
 * 
 * Kelas ini bertanggungjawab dalam me-render background, sedangkan
 * untuk mendapatkan info tentang detil sebuah tile atau yang berkaitan dengan
 * TMX, bisa diakses di field <code>map</code> dari kelas ini.
 * 
 * @author fikr4n
 *
 */
public class TiledBackground extends AbstractTileBackground {
	
	// CONSTANT //
	
	public static final int LAYER_BACKGROUND = 0;
	public static final int LAYER_WALL = 1;
	public static final int LAYER_SELECTION = 2;
	public static final int OBJGROUP_CHAR = 0;
	
	private static final long serialVersionUID = 8095908199451726921L;
	
	// VARIABLE //
	
	public final Map map;
	
	/** Tabel penghalang; (hanya) digunakan oleh algoritma path finding. */
	private boolean[][] t_obstacle;
	
	// CONSTRUCTOR //
	
	public TiledBackground(BaseLoader bsLoader, String fileName) {
		super(0, 0, 0, 0);
		map = new Map(bsLoader, fileName);
		super.setSize(map.getWidth(), map.getHeight());
		super.setTileSize(map.getTileWidth(), map.getTileHeight());
	}
	
	// METHOD //

	@Override
	public void renderTile(Graphics2D g, int tileX, int tileY, int x, int y) {
		int layerCount = map.getLayerCount();
		for (int i = 0; i < layerCount; ++i) {
			BufferedImage tile = map.getTileImage(tileX, tileY, i);
			if (tile != null) {
				g.drawImage(tile, x, y, null);
			}
		}
	}
	
	/**
	 * Lakukan path finding.
	 * @param startX koordinat-x saat ini
	 * @param startY koordinat-y saat ini
	 * @param targetX koordinat-x yang dituju
	 * @param targetY koordinat-y yang dituju
	 * @param obstacle tabel penghalang
	 * @param distance maximum manhattan distance in tile unit
	 * @return node pertama hasil tracing
	 */
	public PathFindingNode trace(int startX, int startY, int targetX, int targetY,
			boolean[][] obstacle, boolean[][] visited, int distance) {
		LinkedList<PathFindingNode> queue = new LinkedList<PathFindingNode>();
		
		// Inisialisasi queue, normalisasi start & target
		queue.add(new PathFindingNode(startX / map.getTileWidth(),
				startY / map.getTileHeight(), null, 0));
		targetX /= map.getTileWidth();
		targetY /= map.getTileHeight();
		// Inisialisasi tabel
		t_obstacle = obstacle;
		for (boolean[] i : visited)
			Arrays.fill(i, false);
		
		// Mulai pencarian
		while (!queue.isEmpty()) {
			PathFindingNode node = queue.poll();
			
			if (node.x == targetX && node.y == targetY) {
				// Yak, ketemu, denormalisasi tile unit ke pixel unit, return first
				while (node.prev != null) {
					node.x *= map.getTileWidth();
					node.y *= map.getTileHeight();
					node.prev.next = node;
					node = node.prev;
				}
				return node.next;
			} else if (node.distance > distance) {
				return null;
			}
			
			if (visited[node.x][node.y])
				continue;
			visited[node.x][node.y] = true;
			
			// Proses untuk selanjutnya
			int d = node.distance + 1;
			if (node.x > 0 && !isObstacle(node.x - 1, node.y)) {
				queue.add(new PathFindingNode(node.x - 1, node.y, node, d));
			}
			if (node.x + 1 < map.getWidth() && !isObstacle(node.x + 1, node.y)) {
				queue.add(new PathFindingNode(node.x + 1, node.y, node, d));
			}
			if (node.y + 1 < map.getHeight() && !isObstacle(node.x, node.y + 1)) {
				queue.add(new PathFindingNode(node.x, node.y + 1, node, d));
			}
			if (node.y > 0 && !isObstacle(node.x, node.y - 1)) {
				queue.add(new PathFindingNode(node.x, node.y - 1, node, d));
			}
		}
		
		// Tidak ditemukan
		return null;
	}

	/**
	 * Apakah pada koordinat tertentu terdapat penghalang.
	 * @param x koordinat-x
	 * @param y koordinat-y
	 * @return <code>true</code> jika merupakan penghalang
	 */
	public boolean isObstacle(int x, int y) {
		return map.getTileId(x, y, LAYER_WALL) != 0 || t_obstacle[x][y];
	}
	
}
